package com.probox.gateway.filter;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.probox.common.core.constant.CacheConstants;
import com.probox.common.core.constant.CachePrefix;
import com.probox.common.core.constant.Constants;
import com.probox.common.core.domain.R;
import com.probox.common.core.utils.SensitiveWordFilter;
import com.probox.common.core.utils.ServletUtils;
import com.probox.common.core.utils.StringUtils;
import com.probox.common.core.utils.ip.IpUtils;
import com.probox.common.redis.service.RedisService;
import com.probox.gateway.config.properties.IgnoreWhiteProperties;
import com.probox.platform.api.RemotePlatformService;
import com.probox.platform.api.domain.PlatformInfoVo;
import com.probox.systemApi.RemoteUserService;
import com.probox.wechat.api.RemoteWechatService;
import com.probox.wechat.api.domain.WechatAuthorizationInfoVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 网关鉴权
 *
 * @author usoft
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);


    //后端用户的token过期时间
    private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;
    //前端客户的token过期时间
    private final static long CUSTOMER_EXPIRE_TIME = Constants.CUSTOMER_TOKEN_EXPIRE * 60;


    @Autowired
    private IgnoreWhiteProperties ignoreWhite;


    @Resource(name = "stringRedisTemplate")
    private ValueOperations<String, String> sops;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RemoteWechatService remoteWechatService;

    @Autowired
    private RemotePlatformService remotePlatformService;

    @Autowired
    private SensitiveWordFilter sensitiveWordFilter;

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 获取平台信息
     *
     * @param appId
     * @return
     */
    private WechatAuthorizationInfoVo getPlatformByAppId(String appId) {
        String key = CachePrefix.PRE_GLOABLE + "WechatAuthorizationInfoVo_" + appId;
        WechatAuthorizationInfoVo rst = redisService.get(key, WechatAuthorizationInfoVo.class);
        if (rst == null) {
            R<WechatAuthorizationInfoVo> r = remoteWechatService.getAuthorizationInfo(appId);
            if (r == null || r.getData() == null) {
                return null;
            }
            rst = r.getData();
            redisService.set(key, rst, RedisService.FIVEMINUTE, TimeUnit.SECONDS);
        }
        return rst;
    }


    /**
     * 获取平台信息
     *
     * @param platformUid
     * @return
     */
    private PlatformInfoVo getPlatformById(Long platformUid) {
        String key = CachePrefix.PRE_GLOABLE + "WechatAuthorizationInfoVo_" + platformUid;
        PlatformInfoVo rst = redisService.get(key, PlatformInfoVo.class);
        if (rst == null) {
            R<PlatformInfoVo> platform = remotePlatformService.getPlatform(platformUid);
            if (platform == null || platform.getData() == null) {
                return null;
            }
            rst = platform.getData();
            redisService.set(key, rst, RedisService.FIVEMINUTE, TimeUnit.SECONDS);
        }
        return rst;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String url = exchange.getRequest().getURI().getPath();
        // 跳过不需要验证的路径
        if (StringUtils.matches(url, ignoreWhite.getWhites())) {
            return chain.filter(exchange);
        }


        if (url.contains("/getOpenId")){
            return chain.filter(exchange);
        }

        String customerAccessPort = getCustomerAccessPort(exchange.getRequest());
        String token = null;
        //如果前端客户访问端口不为空，为空是后端访问
        if (StringUtils.isEmpty(customerAccessPort)) {
            token = getToken(exchange.getRequest());
            //后端用户登录验证
            if (!StringUtils.isBlank(token)) {
                String userStr = sops.get(getTokenKey(token));

                if (StringUtils.isNull(userStr)) {
                    return setUnauthorized501Response(exchange, "登录状态已过期");
                }

                JSONObject obj = JSONObject.parseObject(userStr);
                String userid = obj.getString("userid");

                if (exchange.getRequest().getMethod().matches(HttpMethod.POST.toString())
                        || exchange.getRequest().getMethod().matches(HttpMethod.PUT.toString())
                ) {
                    if (!StringUtils.matches(url, Collections.singletonList("/prod-api/system/plan/add")) )
                    if (haveRepeatRequest(userid, url)) {
                        return setRepeatResponse(exchange, "请勿重复提交");
                    }
                }
                //获取平台Id
                JSONObject sysUser = obj.getJSONObject("sysUser");
                String platformUid = null;
                String supplierUid = null;
                String organId = null;
                String storeId = null;
                //医生ID
                String doctorId = null;

                if (sysUser.get("platformUid") != null) {
                    platformUid = sysUser.get("platformUid").toString();
                }

                if (obj.get("orgUid") != null) {
                    organId = obj.get("orgUid").toString();
                }
                if (sysUser.get("storeId") != null) {
                    storeId = sysUser.get("storeId").toString();
                }
                if (obj.get("doctorId") != null) {
                    doctorId = obj.get("doctorId").toString();
                }
                //log.info("平台id=>"+platformUid);
                String username = obj.getString("username");
                if (StringUtils.isBlank(userid) || StringUtils.isBlank(username)) {
                    return setUnauthorizedResponse(exchange, "令牌验证失败");
                }

                // 设置过期时间
                redisService.expire(getTokenKey(token), EXPIRE_TIME);
                // 设置用户信息到请求
                ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(CacheConstants.DETAILS_USER_ID, userid)
                        .header(CacheConstants.DETAILS_USERNAME, ServletUtils.urlEncode(username)).header(CacheConstants.PLATFORM_HEADER, platformUid)
                        .header(CacheConstants.SUPPLIER_UID, supplierUid).header(CacheConstants.ORGAN_ID, organId)
                        .header(CacheConstants.DETAILS_STORE_ID, storeId).header(CacheConstants.DETAILS_DOCTOR_ID, doctorId).build();
                ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();

                return chain.filter(mutableExchange);
            }
            return setUnauthorizedResponse(exchange, "缺少访问令牌");
        } else {
            //前端客户访问
            String appId = getCustomerAppId(exchange.getRequest());
            if (StringUtils.isEmpty(appId)) {
                return setUnauthorizedResponse(exchange, "缺少appId信息");
            }
            /*
            WechatAuthorizationInfoVo appInfo = getPlatformByAppId(appId);
            if (appInfo == null) {
                return setUnauthorized403Response(exchange, "没有找到应用的信息");
            }
            if (ObjectUtil.isEmpty(appInfo.getPlatformId())) {
                return setUnauthorizedResponse(exchange, "没有绑定平台");
            }*/

//            PlatformInfoVo platformById = getPlatformById(Long.valueOf(appInfo.getPlatformId()));
//            if (ObjectUtil.isEmpty(platformById))
//                return setUnauthorized403Response(exchange, "没有找到平台信息");
//            if (ObjectUtil.isEmpty(platformById.getAuthTime())) {
//                return setUnauthorized403Response(exchange, "没有找到平台信息");
//            }
//            if (DateUtil.date().compareTo(platformById.getAuthTime()) > 0) {
//                return setUnauthorized403Response(exchange, "当前店铺使用时间已结束");
//            }

            //获取前端用户的访问端口
            String sourcePort = getCustomerAccessPort(exchange.getRequest());
            String clientIp = IpUtils.getIpAddr(exchange.getRequest());
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate().header(CacheConstants.CUSTOMER_IP, clientIp).header(CacheConstants.CUSTOMER_APP_ID, appId)
                    .header(CacheConstants.CUSTOMER_PORT, sourcePort).header(CacheConstants.CUSTOMER_APP_ID, appId).header(CacheConstants.CUSTOMER_MCH_ID, "mchId")
                    .header(CacheConstants.CUSTOMER_IP, clientIp).header(CacheConstants.CUSTOMER_PORT, sourcePort).header(CacheConstants.CUSTOMER_PLATFORM,"1");
            token = this.getCustomerToken(exchange.getRequest());
            if (!StringUtils.isBlank(token)) {
                String userStr = sops.get(this.getCustomerTokenKey(token));
                //logger.info("查询到用户信息=>" + userStr);
                if (!StringUtils.isNull(userStr)) {
                    JSONObject obj = JSONObject.parseObject(userStr);
                    String userUid = null;
                    String orgUid = null;
                    String dataStatus = null;
                    if (obj.containsKey("status")) {
                        dataStatus = obj.getString("status");
                    }
                    if (obj.containsKey("orgUid")) {
                        orgUid = obj.getString("orgUid");
                    }
                    if (obj.containsKey("customerUid")) {
                        userUid = obj.getString("customerUid");
                    }
                    String userAccountUid = null;
                    if (obj.containsKey("customerAccountUid")) {
                        userAccountUid = obj.getString("customerAccountUid");
                    }
                    String secretKey = "";
                    if (obj.containsKey("secretKey")) {
                        secretKey = obj.getString("secretKey");
                    }
                    String openId = "";
                    if (obj.containsKey("openId")) {
                        openId = obj.getString("openId");
                    }

                    String userName = null;
                    if (obj.containsKey("createName")) {
                        try {
                            userName = URLEncoder.encode(obj.getString("createName"), "utf-8");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    //logger.info("查询到createname=>" + userName);
                    // 设置过期时间
                    redisService.expire(getCustomerTokenKey(token), CUSTOMER_EXPIRE_TIME);
                    // 设置用户信息到请求
                    builder = builder.header(CacheConstants.CUSTOMER_ID, userUid)
                            .header(CacheConstants.CUSTOMER_SECRET_KEY, secretKey).header(CacheConstants.CUSTOMER_USERNAME, userName)
                            .header(CacheConstants.CUSTOMER_ACCOUNT_ID, userAccountUid).header(CacheConstants.CUSTOMER_OPEN_ID, openId).header(CacheConstants.CUSTOMER_ORG_ID, orgUid).header(CacheConstants.CUSTOMER_ACCOUNT_STATUS, dataStatus);
                }
            } else {

            }
            ServerHttpRequest mutableReq = builder.build();
            return chain.filter(exchange.mutate().request(mutableReq).build());
//            HttpMethod method = exchange.getRequest().getMethod();
//            if (checkUrl(url)) {
//                String obj = sops.get(CachePrefix.SENSTIVEWORD);
//                if (ObjectUtil.isNotEmpty(obj) && "POST".equals(method.toString())) {
//                    obj = obj.replaceAll("\\\\", "");
//                    obj = obj.substring(1, obj.length() - 1);
//                    JSONObject jsonObject = JSONObject.parseObject(obj);
//                    return operationExchange(exchange, chain, exchange.mutate().request(mutableReq).build(), jsonObject.getString("contents"));
//                } else {
//                    return chain.filter(exchange.mutate().request(mutableReq).build());
//                }
//            } else {
//
//            }
        }

    }

    //过滤关建字
    private Mono<Void> operationExchange(ServerWebExchange exchange, GatewayFilterChain chain, ServerWebExchange mutableExchange, String obj) {
        // mediaType

        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        // read & modify body
        ServerRequest serverRequest = new DefaultServerRequest(exchange);
        AtomicReference<Integer> num = new AtomicReference<>(0);
        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                .flatMap(body -> {
                    if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
                        // 拿到body
                        sensitiveWordFilter.initializationWork(obj);
                        String sensitive = sensitiveWordFilter.filter(body);
                        if (sensitive != null && sensitive.trim().length() > 0) {
                            num.getAndSet(num.get() + 1);
                        }
                    }
                    return Mono.empty();
                });
        BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        headers.remove(HttpHeaders.CONTENT_LENGTH);
        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
        return bodyInserter.insert(outputMessage, new BodyInserterContext())
                .then(Mono.defer(() -> {
                    if (num.get() > 0) {
                        return setRepeat500Response(exchange, "包含敏感词,不能提交!");
                    }
                    return chain.filter(mutableExchange);
                }));
    }

    public final static String COMMENTURL = "/api/commentApi/addComment";
    public final static String ACTIVITYURL = "/api/activity";
    public final static String LOCATIONURL = "/api/location";
    public final static String CONTENTURL = "/api/pushApi/pushContent";


    private boolean checkUrl(String url) {
        if (COMMENTURL.equals(url)) {
            return true;
        }
        if (url.indexOf(ACTIVITYURL) > -1) {
            return true;
        }
        if (url.indexOf(LOCATIONURL) > -1) {
            return true;
        }
        if (CONTENTURL.equals(url)) {
            return true;
        }
        return false;
    }

    @Override
    public int getOrder() {
        return -1;
    }


    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(msg)));
        }));
    }

    private Mono<Void> setUnauthorized501Response(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(501, msg)));
        }));
    }


    /**
     * 判断用户是否重复提交
     *
     * @param userId
     * @param url
     * @return
     */
    private boolean haveRepeatRequest(String userId, String url) {
        boolean rst = false;
        String key = CachePrefix.PRE_GLOABLE + "Request_URL_" + userId + "_" + url;
        if (redisService.incr(key, 1, TimeUnit.SECONDS) > 1) {
            rst = true;
        }
        return rst;
    }


    /**
     * 重复请求
     *
     * @param exchange
     * @return
     */
    private Mono<Void> setRepeatResponse(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);
        log.error("[重复提交请求]请求路径:{}", exchange.getRequest().getPath());
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(555, msg)));
        }));
    }

    private Mono<Void> setRepeat500Response(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);
        log.error("[重复提交请求]请求路径:{}", exchange.getRequest().getPath());
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(500, msg)));
        }));
    }

    private Mono<Void> setUnauthorized403Response(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(403, msg)));
        }));
    }


    private String getTokenKey(String token) {
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }

    /**
     * 获取客户的缓存前缀
     *
     * @param token
     * @return
     */
    private String getCustomerTokenKey(String token) {
        return CacheConstants.LOGIN_TOKEN_KEY_CUSTOMER + token;
    }

    /**
     * 获取请求token，获取后端用户的token
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(CacheConstants.HEADER);
        if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)) {
            token = token.replace(CacheConstants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 获取前端客户的token
     *
     * @param request
     * @return
     */
    private String getCustomerToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(CacheConstants.HEADER_CUSTOMER);
        return token;
    }

    /**
     * 获取前端客户的appId
     *
     * @param request
     * @return
     */
    private String getCustomerAppId(ServerHttpRequest request) {
        return request.getHeaders().getFirst(CacheConstants.CUSTOMER_APP_ID);
    }

    /**
     * 获取前端客户访问来源，前端客户访问一定要传此值
     *
     * @param request
     * @return
     */
    private String getCustomerAccessPort(ServerHttpRequest request) {
        String port = request.getHeaders().getFirst(CacheConstants.HEADER_CUSTOMER_PORT);
        return port;
    }


//    @Override
//    public int getOrder() {
//        return -200;
//    }
}
